package lz.controls
{
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.FocusEvent;
	import flash.events.KeyboardEvent;
	import flash.events.TextEvent;
	import flash.text.TextField;
	import flash.text.TextFieldType;
	import flash.text.TextFormat;
	import flash.text.TextLineMetrics;
	import flash.ui.Keyboard;
	
	import lz.controls.core.InvalidationType;
	import lz.controls.core.UIComponent;
	import lz.controls.events.ComponentEvent;
	import lz.controls.managers.IFocusManager;
	import lz.controls.managers.IFocusManagerComponent;

	/**
	 * TextInput 组件是单行文本组件，其中包含本机 ActionScript TextField 对象。
	 * 在应用程序中，可以启用或禁用 TextInput 组件。 TextInput 组件被禁用以后，便无法接收来自鼠标或键盘的输入。 启用的 TextInput 组件像 ActionScript TextField 对象一样可以实现焦点、选择和导航。
	 * 可以使用样式对 TextInput 组件进行自定义，例如，可以在禁用该组件以后更改其外观。 可应用于该组件的一些其他自定义包括使用 HTML 对其进行格式设置，或者将其设置为文本必须隐藏的密码字段。
	 */
	public class TextInput extends UIComponent implements IFocusManagerComponent
	{
		/**
		 * 对 TextInput 组件的内部文本字段的引用。
		 */
		public var textField:TextField;
		protected var _editable:Boolean=true;
		protected var background:DisplayObject;
		/**
		 * @private (protected)
		 */
		protected var _html:Boolean=false;
		/**
		 * @private (protected)
		 */
		protected var _savedHTML:String;
		/**
		 * 默认样式
		 */
		private static var defaultStyles:Object={upSkin: "TextInput_upSkin", disabledSkin: "TextInput_disabledSkin", focusRectSkin: null, focusRectPadding: null, textFormat: null, disabledTextFormat: null, textPadding: 0, embedFonts: false};
		/**
		 * 检索当前组件的默认样式映射。
		 */
		public static function getStyleDefinition():Object
		{
			return defaultStyles;
		}
		/**
		 *  
		 */
		public static var createAccessibilityImplementation:Function;

		public function TextInput()
		{
			super();
		}
		/**
		 * 获取或设置字符串，其中包含当前 TextInput 组件中的文本。 
		 * 此属性包含无格式文本，不包含 HTML 标签。 若要检索格式为 HTML 的文本，请使用 htmlText 属性。
		 * 默认值为 ""。
		 */
		public function get text():String
		{
			return textField.text;
		}
		public function set text(value:String):void
		{
			textField.text=value;
			_html=false;
			invalidate(InvalidationType.DATA);
			invalidate(InvalidationType.STYLES);
		}
		/**
		 * 获取或设置一个值，该值指示组件是否可以接受用户交互。 
		 * true 值指示组件可以接受用户交互；false 值指示组件无法接受用户交互。
		 * 如果将 enabled 属性设置为 false，则容器的颜色将变暗，并且禁止用户输入（Label 和 ProgressBar 组件除外）。
		 */
		override public function get enabled():Boolean
		{
			return super.enabled;
		}
		override public function set enabled(value:Boolean):void
		{
			super.enabled=value;
			updateTextFieldType();
		}
		/**
		 * 获取或设置输入法编辑器 (IME) 的模式。 
		 * IME 使用户能够使用 QWERTY 键盘从中文、日文和韩文字符集输入字符。
		 * Flash 在组件获得焦点时将 IME 设置为指定模式，并在组件失去焦点后还原为初始值。
		 * flash.system.IMEConversionMode 类定义此属性的有效值的常量。
		 * 将此属性设置为 null 阻止对组件使用 IME。
		 */
		public function get imeMode():String
		{
			return _imeMode;
		}
		public function set imeMode(value:String):void
		{
			_imeMode=value;
		}
		/**
		 * 获取或设置一个布尔值，指示当文本字段不具有焦点时选定内容的显示方式。
		 * 如果该值设置为 true 且文本字段不具有焦点，Flash Player 将以灰色突出显示文本字段中的选定内容。 
		 * 如果该值设置为 false 且文本字段不具有焦点，则 Flash Player 不会突出显示文本字段中的选定内容。
		 * 默认值为 false。
		 */
		public function get alwaysShowSelection():Boolean
		{
			return textField.alwaysShowSelection;
		}
		public function set alwaysShowSelection(value:Boolean):void
		{
			textField.alwaysShowSelection=value;
		}
		/**
		 *
		 */
		override public function drawFocus(draw:Boolean):void
		{
			if (focusTarget != null)
			{
				focusTarget.drawFocus(draw);
				return;
			}
			super.drawFocus(draw);
		}
		/**
		 * 获取或设置一个布尔值，指示用户能否编辑文本字段。 
		 * true 值指示用户可以编辑文本字段；false 值指示用户不能编辑文本字段。
		 * 默认值为 true。
		 */
		public function get editable():Boolean
		{
			return _editable;
		}
		public function set editable(value:Boolean):void
		{
			_editable=value;
			updateTextFieldType();
		}
		/**
		 * 获取或设置水平滚动条的滑块位置。
		 * 默认值为 0。
		 */
		public function get horizontalScrollPosition():int
		{
			return textField.scrollH;
		}
		public function set horizontalScrollPosition(value:int):void
		{
			textField.scrollH=value;
		}
		/**
		 * 获取一个值，描述文本字段向右滚动可达到的最远位置。
		 */
		public function get maxHorizontalScrollPosition():int
		{
			return textField.maxScrollH;
		}
		/**
		 * 获取 TextInput 组件中的字符数。
		 */
		public function get length():int
		{
			return textField.length;
		}
		/**
		 * 获取或设置用户可以在文本字段中输入的最大字符数。
		 * 默认值为 0。
		 */
		public function get maxChars():int
		{
			return textField.maxChars;
		}
		public function set maxChars(value:int):void
		{
			textField.maxChars=value;
		}
		/**
		 * 获取或设置一个布尔值，该值指示当前创建的 TextInput 组件实例用于包含密码还是文本。 
		 * true 值指示组件实例为密码文本字段；false 值指示组件实例为正常文本字段。
		 * 将此属性设置为 true 时，对于用户在文本字段中输入的每个字符，TextInput 组件实例将显示星号。
		 * 此外，“剪切”和“复制”命令及其键盘快捷键将被禁用。这些措施可防止有人从无人看管的计算机上获取密码。
		 * 默认值为 false。
		 */
		public function get displayAsPassword():Boolean
		{
			return textField.displayAsPassword;
		}
		public function set displayAsPassword(value:Boolean):void
		{
			textField.displayAsPassword=value;
		}
		/**
		 * 获取或设置文本字段从用户处接受的字符串。 
		 * 请注意，未包含在本字符串中的以编程方式输入的字符也为文本字段所接受。
		 * 字符串中字符的读取顺序为从左到右。 
		 * 可以使用连字符 (-) 指定一个字符范围。
		 * 如果此属性的值为 null，则文本字段会接受所有字符。 
		 * 如果此属性设置为空字符串 ("")，则文本字段不接受任何字符。
		 * 如果字符串以尖号 (^) 开头，则先接受所有字符，然后从接受字符集中排除字符串中 ^ 之后的字符。
		 * 如果字符串不以尖号 (^) 开头，则最初不接受任何字符，然后将字符串中的字符包括在接受字符集中。
		 * 默认值为 null。
		 */
		public function get restrict():String
		{
			return textField.restrict;
		}
		public function set restrict(value:String):void
		{
			if (componentInspectorSetting && value == "")
			{
				value=null;
			}
			textField.restrict=value;
		}
		/**
		 * 获取包含一个或多个字符的选定内容中第一个所选字符的索引值。
		 * 所选字符的索引位置为从零开始并由文本区域中显示的第一个字符开始计算。 
		 * 如果未选择任何内容，则该值将设置为尖号的位置。
		 * 默认值为 0。
		 */
		public function get selectionBeginIndex():int
		{
			return textField.selectionBeginIndex;
		}
		/**
		 * 获取包含一个或多个字符的选定内容中最后一个所选字符的索引位置。
		 * 所选字符的索引位置为从零开始并由文本区域中显示的第一个字符开始计算。 
		 * 如果未选择任何内容，则该值将设置为尖号的位置。
		 * 默认值为 0。
		 */
		public function get selectionEndIndex():int
		{
			return textField.selectionEndIndex;
		}
		/**
		 * 获取或设置一个布尔值，该值指示是否从包含 HTML 文本的 TextInput 组件中删除额外空白。 
		 * 空格和换行符都属于组件中的额外空白。 
		 * true 值指示删除多余的空白；false 值指示不删除多余的空白。
		 * 此属性只影响使用 htmlText 属性设置的文本，而不影响使用 text 属性设置的文本。 
		 * 如果使用 text 属性设置文本，则忽略 condenseWhite 属性。
		 * 如果将 condenseWhite 属性设置为 true，则必须使用标准 HTML 命令（如 <br> 和 <p>）将换行符放在文本字段中。
		 * 默认值为 false。
		 */
		public function get condenseWhite():Boolean
		{
			return textField.condenseWhite;
		}
		public function set condenseWhite(value:Boolean):void
		{
			textField.condenseWhite=value;
		}
		/**
		 * 包含文本字段所含字符串的 HTML 表示形式。
		 * 默认值为 ""。
		 */
		public function get htmlText():String
		{
			return textField.htmlText;
		}
		public function set htmlText(value:String):void
		{
			if (value == "")
			{
				text="";
				return;
			}
			_html=true;
			_savedHTML=value;
			textField.htmlText=value;
			invalidate(InvalidationType.DATA);
			invalidate(InvalidationType.STYLES);
		}
		/**
		 * 文本的高度，以像素为单位。
		 */
		public function get textHeight():Number
		{
			return textField.textHeight;
		}
		/**
		 * 文本的宽度，以像素为单位。
		 */
		public function get textWidth():Number
		{
			return textField.textWidth;
		}
		/**
		 * 设置在具有焦点的文本区域中选定内容的范围。 选定内容的范围从由 start 参数指定的索引处开始，到由 end 参数指定的索引处结束。 如果指定选定内容范围的参数值相同，则此方法会设置文本插入点，设置方式与 caretIndex 属性相同。
		 * 选定的文本被视为从零开始的一串字符，其中第一个选定字符位于索引 0 处，第二个字符位于索引 1 处，依此类推。
		 * 如果文本字段不具有焦点，则此方法无效。
		 * 参数
		 * beginIndex:int — 选定内容中第一个字符的索引位置。
		 * endIndex:int — 选定内容中最后一个字符的索引位置。
		 */
		public function setSelection(beginIndex:int, endIndex:int):void
		{
			textField.setSelection(beginIndex, endIndex);
		}
		/**
		 * 检索有关指定文本行的信息。
		 * 参数
		 * index:int — 要检索的信息所在行的行号。
		 */
		public function getLineMetrics(index:int):TextLineMetrics
		{
			return textField.getLineMetrics(index);
		}
		/**
		 * 将指定字符串追加到 TextArea 所包含的最后一个字符之后。
		 * 与通过对 text 属性使用加法赋值来连接两个字符串（例如 myTextArea.text += moreText）相比，此方法的效率更高。此方法在 TextArea 组件包含大量内容时特别有用。
		 * 参数
		 * text:String — 要追加到现有文本末尾的字符串。
		 */
		public function appendText(text:String):void
		{
			textField.appendText(text);
		}
		/**
		 * 
		 */
		protected function updateTextFieldType():void
		{
			textField.type=(enabled && editable) ? TextFieldType.INPUT : TextFieldType.DYNAMIC;
			textField.selectable=enabled;
		}
		/**
		 */
		protected function handleKeyDown(event:KeyboardEvent):void
		{
			if (event.keyCode == Keyboard.ENTER)
			{
				dispatchEvent(new ComponentEvent(ComponentEvent.ENTER, true));
			}
		}
		/**
		 */
		protected function handleChange(event:Event):void
		{
			event.stopPropagation(); // so you don't get two change events
			dispatchEvent(new Event(Event.CHANGE, true));
		}
		/**
		 * 
		 */
		protected function handleTextInput(event:TextEvent):void
		{
			event.stopPropagation();
			dispatchEvent(new TextEvent(TextEvent.TEXT_INPUT, true, false, event.text));
		}
		/**
		 */
		protected function setEmbedFont():void
		{
			var embed:Object=getStyleValue("embedFonts");
			if (embed != null)
			{
				textField.embedFonts=embed;
			}
		}
		/**
		 * 绘制
		 */
		override protected function draw():void
		{
			if (isInvalid(InvalidationType.STYLES, InvalidationType.STATE))
			{
				drawTextFormat();
				drawBackground();

				var embed:Object=getStyleValue('embedFonts');
				if (embed != null)
				{
					textField.embedFonts=embed;
				}

				invalidate(InvalidationType.SIZE, false);
			}
			if (isInvalid(InvalidationType.SIZE))
			{
				drawLayout();
			}

			super.draw();
		}
		/**
		 * 绘制背景
		 */
		protected function drawBackground():void
		{
			var bg:DisplayObject=background;

			var styleName:String=(enabled) ? "upSkin" : "disabledSkin";
			background=getDisplayObjectInstance(getStyleValue(styleName));
			if (background == null)
			{
				return;
			}
			addChildAt(background, 0);

			if (bg != null && bg != background && contains(bg))
			{
				removeChild(bg);
			}
		}
		/**
		 * 绘制文本格式
		 */
		protected function drawTextFormat():void
		{
			// Apply a default textformat
			var uiStyles:Object=UIComponent.getStyleDefinition();
			var defaultTF:TextFormat=enabled ? uiStyles.defaultTextFormat as TextFormat : uiStyles.defaultDisabledTextFormat as TextFormat;
			textField.setTextFormat(defaultTF);

			var tf:TextFormat=getStyleValue(enabled ? "textFormat" : "disabledTextFormat") as TextFormat;
			if (tf != null)
			{
				textField.setTextFormat(tf);
			}
			else
			{
				tf=defaultTF;
			}
			textField.defaultTextFormat=tf;

			setEmbedFont();
			if (_html)
			{
				textField.htmlText=_savedHTML;
			}
		}
		/**
		 * 绘制布局
		 */
		protected function drawLayout():void
		{
			var txtPad:Number=Number(getStyleValue("textPadding"));
			if (background != null)
			{
				background.width=width;
				background.height=height;
			}
			textField.width=width - 2 * txtPad;
			textField.height=height - 2 * txtPad;
			textField.x=textField.y=txtPad;
		}
		/**
		 * 配置UI
		 */
		override protected function configUI():void
		{
			super.configUI();
			tabChildren=true;
			textField=new TextField();
			addChild(textField);
			updateTextFieldType();
			textField.addEventListener(TextEvent.TEXT_INPUT, handleTextInput, false, 0, true);
			textField.addEventListener(Event.CHANGE, handleChange, false, 0, true);
			textField.addEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown, false, 0, true);
		}
		/**
		 * 
		 */
		override public function setFocus():void
		{
			stage.focus=textField;
		}
		/**
		 * 
		 */
		override protected function isOurFocus(target:DisplayObject):Boolean
		{
			return target == textField || super.isOurFocus(target);
		}
		/**
		 * 
		 */
		override protected function focusInHandler(event:FocusEvent):void
		{
			if (event.target == this)
			{
				stage.focus=textField;
			}
			var fm:IFocusManager=focusManager;
			if (editable && fm)
			{
				fm.showFocusIndicator=true;
				if (textField.selectable && textField.selectionBeginIndex == textField.selectionBeginIndex)
				{
					setSelection(0, textField.length);
				}
			}
			super.focusInHandler(event);

			if (editable)
			{
				setIMEMode(true);
			}
		}
		/**
		 * 
		 */
		override protected function focusOutHandler(event:FocusEvent):void
		{
			super.focusOutHandler(event);

			if (editable)
			{
				setIMEMode(false);
			}
		}
	}
}
